package com.lj.universalframework.http


import androidx.lifecycle.*
import com.lj.universalframework.app.BaseViewModel
import com.lj.universalframework.app.MyApplication
import com.lj.universalframework.utils.CommonUtil
import com.lj.universalframework.widget.ToastShow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import retrofit2.HttpException
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

/**
 * Created by PengFeifei on 2019-12-24.
 *
 * https://developer.android.google.cn/topic/libraries/architecture/coroutines#livedata
 */
open class RequestViewModel : BaseViewModel() {

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        //状态发生改变
    }

    val service by lazy { Request.apiService(RetrofitService::class.java) }

    private fun <Response> api(apiDSL: ViewModelDsl<Response>.() -> Unit) {
        //开启一个协程
        ViewModelDsl<Response>().apply(apiDSL).launch(viewModelScope)
    }


    protected fun <Response> apiCallback(
        request: suspend () -> Response,
        onResponse: ((Response) -> Unit),
        onStart: (() -> Boolean)? = null,
        onError: ((java.lang.Exception) -> Boolean)? = null,
        onFinally: (() -> Boolean)? = null
    ) {

        api<Response> {
            onRequest {
                //请求时
                request.invoke()
            }

            onResponse {
                //响应时
                onResponse.invoke(it)
            }

            onStart {
                onStart?.invoke()
            }

            onError {
                onError?.invoke(it)
            }

            onFinally {
                onFinally?.invoke()
            }
        }
    }


    /**
     * 常用请求
     * mContext 上下文
     * toast 是否吐司
     */
    protected fun <Response> apiRequest(
        apiDSL: ViewModelDsl<Response>.() -> Unit,
        showDialog: Boolean = true
    ) {
        //检查当前是否有网
        if (!CommonUtil.isNetworkConnected(MyApplication.mContext)) {
            ToastShow.err(content = "网络连接失败，请检查网络后重试")
            ViewModelDsl<Response>().apply(apiDSL).onError?.invoke(Exception("网络连接失败，请检查网络后重试"))
            return
        }
        api<Response> {
            onStart {
                if (showDialog) {
                    //请求前开启加载中弹框
                    _loadingEvent.value = true
                }
                ViewModelDsl<Response>().apply(apiDSL).onStart?.invoke()
            }
            onRequest {
                ViewModelDsl<Response>().apply(apiDSL).request()
            }
            onResponse {
                ViewModelDsl<Response>().apply(apiDSL).onResponse?.invoke(it)
            }
            onError { error ->
                if (error is HttpException) {
                    when (error.code()) {
                        in 400..499 -> ToastShow.err(content = "网络请求出现异常")
                        in 500..599 -> ToastShow.err(content = "服务器升级维护，正在提升用户体验！")
                    }
                } else if (error.toString() == SOCKET || error.toString().contains(CONNECT)) {
                    ToastShow.err(content = "网络无法连接或连接超时")
                } else if (error.toString().contains(SOCKETTIME)) {
                    ToastShow.err(content = "无法连接到服务器")
                } else {
                    ToastShow.err(content = "网络请求异常")
                }
                ViewModelDsl<Response>().apply(apiDSL).onError?.invoke(error)
            }
            onFinally {
                if (showDialog) {
                    //请求后关闭加载中弹框
                    _loadingEvent.value = false
                }
                ViewModelDsl<Response>().apply(apiDSL).onFinally?.invoke()
            }
        }
    }


    /**
     * 判断异常类型  异常状态  1-->网络原因 2-->服务器原因
     */
    protected fun onErrorType(error: Exception): Int {
        if (error is HttpException) {
            when (error.code()) {
                //网络请求出现异常
                in 400..499 -> return 1
                //服务器异常
                in 500..599 -> return 2
            }
        }
        if (error.toString() == SOCKET || error.toString().contains(CONNECT)) {
            //网络无法连接或连接超时
            return 1
        }
        if (error.toString().contains(SOCKETTIME)) {
            //无法连接到服务器
            return 2
        }
        return 1
    }


    protected fun <Response> apiLiveData(
        context: CoroutineContext = EmptyCoroutineContext,
        timeoutInMs: Long = 3000L,
        request: suspend () -> Response
    ): LiveData<Result<Response>> {
        return liveData(context, timeoutInMs) {
            emit(Result.Start())
            try {
                emit(withContext(Dispatchers.IO) {
                    Result.Response(request())
                })
            } catch (e: Exception) {
                e.printStackTrace()
                emit(Result.Error(e))
            } finally {
                emit(Result.Finally())
            }
        }
    }
}
